#include "client_shell.h"
#include "network.h"
#include "filesys.h"
#include"cmd_code.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <ctype.h>
#include<fcntl.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<libgen.h>


int gl_isConnected = 0;
int gl_servfd = -1;
char gl_servIP[16];
char clnt_cur_dir[DIR_MAX_SIZE] = CLNT_ROOT_DIR;
int dir_depth = 0;

void runShell(){
    char inpbuf[BUFFER_SIZE];
    while(1){
        printf("ftp>");
        char *cmd_str = getCmdStr((char *)&inpbuf);
        if(cmd_str == NULL)
            continue;
        int cmd_code = getCmdCode(cmd_str); 
        matchCmd(cmd_code,getParam(cmd_str));
    }
}

char *getCmdStr(char *inpbuf){
    fgets(inpbuf,BUFFER_SIZE,stdin);
    if(inpbuf[0] == '\n')
        return NULL;
    inpbuf[strlen(inpbuf) - 1] = '\0';
    //去掉前导空格
    while(*inpbuf == ' ')
        inpbuf++;
    return inpbuf;
}

int getCmdCode(char *cmd_str){
    if(!strcmp(cmd_str,"help")){
        return CMD_HELP;
    }
    else if(!strcmp(cmd_str,"exit") || !strcmp(cmd_str,"quit")){
        return CMD_EXIT;
    }
    else if(!strncmp(cmd_str,"open",4)){
        return CMD_OPEN;
    }
    else if(!strcmp(cmd_str,"close")){
        return CMD_CLOSE;
    }
    else if(!strcmp(cmd_str,"ls")){
        return CMD_LS;
    }
    else if(!strncmp(cmd_str,"cd",2)){
        return CMD_CD;
    }
    else if(!strncmp(cmd_str,"lcd",3)){
        return CMD_LCD;
    }
    else if(!strncmp(cmd_str,"put",3)){
        return CMD_PUT;
    }
    else if(!strncmp(cmd_str,"get",3)){
        return CMD_GET;
    }
    else if(!strncmp(cmd_str,"mkdir",5)){
        return CMD_MKDIR;
    }
    else if(!strncmp(cmd_str,"rmdir",5)){
        return CMD_RMDIR;
    }
    else if(!strcmp(cmd_str,"echo")){
        return CMD_ECHO;
    }
    else{
        return CMD_ERR;
    }

}

char *getParam(char *cmd_str){
    char *arg_str = strchr(cmd_str,' ');
    if(arg_str == NULL){
        return NULL;
    }
    arg_str++;
    cmd_str = arg_str;
    return cmd_str;
}

int isVaildIP(char *ip_str){
    int dotNum = 0;
    int n = strlen(ip_str);
    if(!isdigit(ip_str[n - 1]))
        return 0;
    int num = 0, pow = 1;
    for(int i = n - 1; i >= 0; i--){
        if(ip_str[i] != '.'){
            num += (ip_str[i] - '0') * pow;
            pow *= 10; 
        }
        else{
            dotNum++;
            if(dotNum > 3)
                return -1;
            if(num > 255 || num < 0)
                return -1;
            num = 0;
        }
    }
    return dotNum == 3;
}

void matchCmd(int cmd_code,char *args){
    switch (cmd_code)
    {
        case CMD_EXIT: 
            ftpExit();
            break;
        case CMD_OPEN:
            ftpRequestOpen(args);
            break;
        case CMD_CLOSE:
            ftpRequestClose();
            break;
        case CMD_LS:
            ftpRequestLs();
            break;
        case CMD_CD:
            ftpRequestCd(args);
            break;
        case CMD_PUT:
            ftpRequestPut(args);
            break;
        case CMD_GET:
            ftpRequestGet(args);
            break;
        case CMD_MKDIR:
            ftpRequestMkdir(args);
            break;
        case CMD_RMDIR:
            ftpRequestRmdir(args);
            break;
        case CMD_HELP:
            ftpHelp();
            break;
        case CMD_ECHO:
            ftpRequestEcho();
            break;
        default:
            printf("invaild command!\n");
            break;
    }
}

int sendRequest(int cmd_code){
    int status = cmd_code;
    write(gl_servfd,&status,sizeof(int));
    read(gl_servfd,&status,sizeof(int));
    return cmd_code;
}

void ftpExit(){
    if(gl_isConnected){
        ftpRequestClose();
    }
    printf("bye\n");
    exit(0);
}

int ftpRequestOpen(char *ip_str){
    if(gl_isConnected){
        printf("已连接至服务器, 请首先断开连接\n");
        return -1;
    }
    if(ip_str == NULL){
        printf("需要主机地址。\n");
        return -1;
    }
    if(!isVaildIP(ip_str)){
        printf("未知主机。\n");
        return -1;
    }   
    gl_servfd = clientConn(ip_str);
    if(gl_servfd == -1){
        printf("连接对应服务器失败, 稍后重试吧\n");
        return -1;
    }
    gl_isConnected = 1;
    strcpy(gl_servIP,ip_str);
    printf("成功连接至%s\n",gl_servIP);
    return 0;
}

int ftpRequestClose(){
    if(!gl_isConnected){
        printf("未连接\n");
        return -1;
    }
    if(sendRequest(CMD_CLOSE) == CMD_ERR)
        printf("close发生未知异常\n");

    printf("已与%s断开连接。\n", gl_servIP);
    gl_isConnected = 0;
    bzero(gl_servIP,sizeof(gl_servIP));
    return 0;
}

int ftpRequestEcho(){  
    if(!gl_isConnected){
        printf("未连接\n");
        return -1;
    }
    if(sendRequest(CMD_ECHO) == CMD_ERR){
        printf("echo error!\n");
        return -1;
    }
    char buf[BUFFER_SIZE];
    while(1){
        printf("say something to server:(q to quit)\n");
        fgets(buf,BUFFER_SIZE,stdin);
        if(!strcmp(buf,"q\n") || !strcmp(buf,"Q\n")){
            char eof = 'q';
            write(gl_servfd,&eof,1);
            break;
        }
        //确定客户端到底写了多少字节来确定读多少字节，避免各种边界问题
        int write_len = write(gl_servfd,buf,strlen(buf));
        int recv_cur = 0;
        while(recv_cur < write_len){
            int recv_cnt = read(gl_servfd,buf,write_len);
            if(recv_cnt <= 0){
                printf("client read error!\n");
                break;
            }
            recv_cur += recv_cnt;
        }
        printf("写入：%d 读出:%d \n", write_len, recv_cur);
        printf("reply from server: %s\n",buf);
    }
    
    return 0;
}

int ftpRequestLs(){
    if(!gl_isConnected){
        printf("未连接\n");
        return -1;
    }
    if(sendRequest(CMD_LS) == CMD_ERR){
        printf("ls error!\n");
        return -1;
    }

    
    char read_buf[BUFFER_SIZE];
    memset(read_buf,0,BUFFER_SIZE);
    int datalen = 0;
    read(gl_servfd,&datalen,sizeof(int));
    read(gl_servfd,read_buf,datalen);
    printf("%s\n",read_buf);
    return 0;
}

int ftpRequestCd(char *sys_path){
    if(!gl_isConnected){
        printf("未连接\n");
        return -1;
    }

    if(sys_path == NULL){
        printf("请指定路径\n");
        return -1;
    }

    if(sendRequest(CMD_CD) == CMD_ERR){
        printf("cd error!");
        return -1;
    }
    write(gl_servfd,sys_path,strlen(sys_path));

    int status, dir_len;
    char newDir[DIR_MAX_SIZE];
    memset(newDir,0,DIR_MAX_SIZE);

    read(gl_servfd,&status,sizeof(int));
    read(gl_servfd,&dir_len,sizeof(int));
    read(gl_servfd,newDir,dir_len);
  
    if(status == FSTAT_DIR_CHANGED){
        printf("路径切换成功\n");
    }
    else if(status == FSTAT_REG){
        printf("这是一个文件路径,请输入对应目录路径\n");
    }
    else if(status == FSTAT_IS_ROOTDIR){
        printf("已在根路径，无法向上\n");
    }
    else if(status == FSTAT_NO_SUCH_FILE){
        printf("找不到所请求的目录\n");
    }
    printf("当前服务端目录:%s\n",newDir);

    return 0;
}

int ftpRequestPut(char *file_path){
    if(!gl_isConnected){
        printf("未连接\n");
        return -1;
    }

    if(file_path == NULL){
        printf("请指定路径\n");
        return -1;
    }


    if(sendRequest(CMD_PUT) == CMD_ERR){
        printf("put error!\n");
        return -1;
    }

    return sendFile(gl_servfd,file_path);
}

int ftpRequestGet(char *file_path){
     if(!gl_isConnected){
        printf("未连接\n");
        return -1;
    }

    if(file_path == NULL){
        printf("请指定路径\n");
        return -1;
    }


    if(sendRequest(CMD_GET) == CMD_ERR){
        printf("get error!\n");
        return -1;
    }
    
    //向服务器询问是否有对应文件
    int status = 0, path_len = strlen(file_path);
    write(gl_servfd,&path_len,sizeof(int));
    write(gl_servfd,file_path,path_len);

    read(gl_servfd,&status,sizeof(int));
    if(status == FSTAT_NO_SUCH_FILE){
        printf("服务器未找到该文件!\n");
        return -1;
    }
    else if(status == FSTAT_DIR){
        printf("不能下载目录\n");
        return -1;
    }
    else{
        printf("获取服务器文件成功\n");
    }
    
    char *fname = basename(file_path);
    printf("打开对应接收流中\n");
    int filefd = open(fname, O_WRONLY | O_TRUNC | O_CREAT , 777);
    printf("filefd = %d\n",filefd);
    if(filefd == -1){
        perror("errno:");
        status = FSTAT_OPEN_ERROR;
        write(gl_servfd,&status,sizeof(int));
        return -1;
    }        
    else{
        perror("errno:");
        status = FSTAT_NOMAL;
        write(gl_servfd,&status,sizeof(int));
  
    }

    //获取文件长
    int fdata_len = 0;
    read(gl_servfd,&fdata_len,sizeof(int));

    printf("开始下载 下载路径:%s 预计长度:%d\n", file_path, fdata_len);
    int recv_cur = 0; char bit; 
    while(recv_cur < fdata_len){
        int recv_cnt = read(gl_servfd,&bit,1);
        if(recv_cnt < 0){
            printf("下载过程中出现错误\n");
            status = FSTAT_GET_ERROR;
            write(gl_servfd,&status,sizeof(int));
            close(filefd);
            return -1;
        }
        else{
            status = FSTAT_NOMAL;
            write(gl_servfd,&status,sizeof(int));
        }

        write(filefd,&bit,1);
        recv_cur += recv_cnt;
        printf("下载:%d 目前:%d\n",recv_cnt,recv_cur);
    }

    printf("文件下载完毕\n");
    close(filefd);
    return 0;

}

int ftpRequestMkdir(char *sys_path){return 0;}

int ftpRequestRmdir(char *sys_path){return 0;}

void ftpHelp(){
    printf("\tls - 显示ftp服务器当前目录文件\n");
    printf("\tcd - 切换远程目录\n");
    printf("\thelp - 显示指令\n");
    printf("\topen - 连接ftp服务器IP\n");
    printf("\tget - 下载文件,可指定目录,默认为根目录\n");
    printf("\tput - 上次文件,可指定目录,默认为根目录\n");
    printf("\texit - 退出\n");
}


void ftpClientStatus(){
    if(gl_isConnected == 0){
        printf("状态: 未连接\n");
    }
    else{
        printf("状态: 已连接 连接IP:%s\n",gl_servIP);
    }
}














